﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using exsportance.Silverlight.Common.Utils;

namespace exsportance.Silverlight.Validate
{
    /// <summary>
    /// Groups a collection of validations
    /// </summary>
    public class ValidationGroup : Dictionary<IValidation, ValidationGroupMode>, IValidation
    {
        #region members
        private bool _breakOnValidationError;
        private FrameworkElement _messageControl;
        private DependencyProperty _messageControlProperty;
        private string _name;
        #endregion

        #region properties
        /// <summary>
        /// The group name
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        #endregion

        #region constructor
        /// <summary>
        /// Create a new validation group.
        /// Breaks if validation of a item returns false
        /// </summary>
        public ValidationGroup()
            : this(true)
        {
        }

        /// <summary>
        /// Create a new validation group.
        /// Breaks if validation of a item returns false
        /// </summary>
        public ValidationGroup(string name)
            : this(true, name)
        {
        }

        /// <summary>
        /// Create a new validation group.
        /// Breaks if validation of a item returns false.
        /// </summary>
        /// <param name="itema">Adds  new items to collection</param>
        public ValidationGroup(params IValidation[] items)
            : this(true, items)
        {
        }

        /// <summary>
        /// Create a new validation group
        /// </summary>
        /// <param name="breakOnValidationError">Break if validation of a item returns false</param>
        public ValidationGroup(bool breakOnValidationError)
            : base()
        {
            _breakOnValidationError = breakOnValidationError;
        }

        /// <summary>
        /// Create a new validation group
        /// </summary>
        /// <param name="breakOnValidationError">Break if validation of a item returns false</param>
        public ValidationGroup(bool breakOnValidationError, string name)
            : this(breakOnValidationError)
        {
            _name = name;
        }

        /// <summary>
        /// Create a new validation group
        /// </summary>
        /// <param name="breakOnValidationError">Break if validation of a item returns false</param>
        /// <param name="items">Adds  new items to collection</param>
        public ValidationGroup(bool breakOnValidationError, params IValidation[] items)
            : this(breakOnValidationError)
        {
            Add(items);
        }

        /// <summary>
        /// Create a new validation group
        /// </summary>
        /// <param name="breakOnValidationError">Break if validation of a item returns false</param>
        /// <param name="name"></param> 
        /// <param name="items">Adds  new items to collection</param>
        public ValidationGroup(bool breakOnValidationError, string name, params IValidation[] items)
            : this(breakOnValidationError, name)
        {
            Add(items);
        }
        #endregion

        /// <summary>
        /// Adds validation items
        /// </summary>
        /// <param name="item"></param>
        public void Add(params IValidation[] items)
        {
            foreach (IValidation item in items)
                Add(item, ValidationGroupMode.And);
        }

        /// <summary>
        /// Executes the item's validators
        /// </summary>
        /// <returns></returns>
        public ValidationResult Validate()
        {
            return Validate(true);
        }

        /// <summary>
        /// Executes the item's validators
        /// </summary>
        /// <param name="allowMessages">Are messages allowed</param>
        /// <returns></returns>
        public ValidationResult Validate(bool allowMessages)
        {
            ValidationResult result = new ValidationResult();

            ValidationMessages lastMessages = null;
            bool lastValueProvided = false;

            // copy to local list -> simpler to algo
            List<IValidation> validationItems = this.GetKeys();
            List<ValidationGroupMode> validationItemModes = this.GetValues();

            for (int i = 0; i < validationItems.Count; i++)
            {
                // validate the item
                ValidationResult tmp = validationItems[i].Validate(allowMessages);

                if (i == 0)
                {
                    result.Result = tmp.Result;
                    result.ValueProvided = tmp.ValueProvided;

                    if (tmp.Result)
                        result.ValidItemName = validationItems[i].Name;
                }
                else
                {
                    if (validationItemModes[i] == ValidationGroupMode.And)
                    {
                        result.Result = result.Result && tmp.Result;
                    }
                    else
                    {
                        result.Result = result.Result || tmp.Result;

                        if (tmp.Result)
                            result.ValidItemName = validationItems[i].Name;
                    }

                    result.ValueProvided = result.ValueProvided || tmp.ValueProvided;
                }

                // check param
                if (!result.Result && _breakOnValidationError)
                {

                    // check if next is OR
                    if (i + 1 < validationItems.Count && validationItemModes[i + 1] == ValidationGroupMode.Or)
                    {
                        // remember the messages
                        lastMessages = tmp.Messages;
                        lastValueProvided = tmp.ValueProvided;
                    }
                    else
                    {
                        // if previous validation failed , add this messages, if value was provided
                        if (lastMessages != null && lastValueProvided)
                            result.Messages.AddRange(lastMessages);
                        else
                            result.Messages.AddRange(tmp.Messages);
                        break;
                    }
                }
                else
                {
                    // add the messages
                    result.Messages.AddRange(tmp.Messages);
                    lastMessages = null;

                    // check if next is AND
                    if (!(i + 1 < validationItems.Count && validationItemModes[i + 1] == ValidationGroupMode.And))
                        // break if next is not AND operator
                        break;
                }
            }

            if (allowMessages)
                showMessages(result);

            return result;
        }

        /// <summary>
        /// Executes the item's validators.
        /// </summary>
        /// <returns></returns>
        public bool IsValid()
        {
            return Validate(false).Result;
        }

        /// <summary>
        /// Set the control where messages are shown
        /// </summary>
        /// <param name="control"></param>
        /// <param name="contentProperty"></param>
        public void SetMessageControl(FrameworkElement control, DependencyProperty contentProperty)
        {
            if (control == null)
                throw new ArgumentNullException("control");

            if (contentProperty == null)
                throw new ArgumentNullException("contentProperty");

            _messageControl = control;
            _messageControlProperty = contentProperty;
        }

        #region private methods
        /// <summary>
        /// Shows the messages if a message control is provided
        /// </summary>
        private void showMessages(ValidationResult result)
        {
            if (_messageControl != null)
            {
                string messages = result.GetMessages();

                _messageControl.SetValue(_messageControlProperty, messages);
            }
        }
        #endregion
    }

    public enum ValidationGroupMode
    {
        And = 1,
        Or = 2,
    }
}
